/**
*    Copyright (C) 2008  Chase Kernan 
*    chase.kernan@gmail.com
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
*/

package com.chasekernan.hxnova.core.components;

import haxe.xml.Check;

/**
    Stores sets of components for hulls. 
    Example: 
        The ["weapon"] type set holds the types [BeamWeapon] and [Missile] so
        that a hull can have either of those types in a slot.
**/
class ComponentTypeSet {
    
    public var types : Array<Class<Dynamic>>;
    public var name : String;
    
    public function new(name : String) {
        this.name = name;
        types = new Array<Class<Dynamic>>();
    }
    
    /**
        Adds a component type.
        Will throw an error if the class does not directly subclass [Component]
        (TODO: Allow a type to be any subclass of [Component]) or if the type
        is already in the type set.
    **/
    public function addType(c : Class<Dynamic>) {
        //check to make sure it extends component
        if(c != Component) {
            var c2 = Type.getSuperClass(c);
            while(c2 != Component) {
                if(c2 == null) throw "Given class does not extend Component: "+c;
                else c2 = Type.getSuperClass(c2);
            }
        }
        
        //check to make sure its not already in the array
        for(i in types) {
            if(c == i) throw "Given class is already in types: " + c;
        }
        types.push(c);
    }
    
    /**
        Using the [ComponentSet::getClassFromXmlName] method, this finds the 
        component with the xml name given and adds it.
        Will throw the same errors as addType.
    **/
    public function addTypeByName(xmlName : String) {
        addType(ComponentSet.getClassFromXmlName(xmlName));
    }
    
    /**
        Returns true if the given type is in the set.
    **/
    public function hasType(c : Class<Dynamic>) : Bool {
        for(i in types) {if(i == c) return true;}
        return false;
    }
    
    /**
        Returns the xml names of all the components in the set.
    **/
    public function getNames() : Array<String> {
        var a = new Array<String>();
        for(i in types) {a.push(ComponentSet.getXmlName(i));}
        return a;
    }
    
    /**
        Returns true if a component in the set has the same xml name as the one
        given.
    **/
    public function hasTypeByName(xmlName : String) : Bool {
         return hasType(ComponentSet.getClassFromXmlName(xmlName));
    }
    
    public function iterator() {
        return new CTSIterator(getNames());
    }
    
    public static var XML_TITLE = "component_type_set";
    
    public static function getXmlRules() : Rule {
        return RNode("component_type_set", [Att("name")], RData());
    }
    
    /**
        Writes a component type set to xml.
        Sample xml:
            TODO: run writeToXml and insert xml here.
    **/
    public static function writeToXml(cts : ComponentTypeSet) : Xml {
        var xml = Xml.parse('<' + XML_TITLE +'>' + cts.getNames().join(',') + 
                            '</' + XML_TITLE + '>').firstChild();
                            
        xml.set('name', cts.name);
        
        Check.checkNode(xml, getXmlRules());
        return xml;
    }
    
    /**
        Reads a componet type set from an xml file.
        Will throw an error if there any mistakes.
    **/
    public static function readFromXml(xml : Xml) : ComponentTypeSet {
        try {
            Check.checkNode(xml, getXmlRules());
            
            var cts = new ComponentTypeSet(xml.get('name'));
            
            for(i in xml.firstChild().nodeValue.split(','))
                cts.addTypeByName(i);
                
            return cts;
        } catch (e : Dynamic) {
            throw "Error while reading from xml file for component type: " + e;
        }
    }
}

class CTSIterator {
    var values : Array<String>;
    var curr : Int;
    
    public function new (values : Array<String>) {
        curr = 0;
        this.values = values;
    }
    
    public function hasNext () {
        return (curr < values.length);
    }
    
    public function next () {
        return values[curr++];
    }
}